package com.six.backend.aspect;


import com.six.backend.annotation.OperLog;
import com.six.backend.utils.ipUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.ArrayList;


/**
 * @Author: skyler
 * @Date: 2021/11/8 11:16
 */
@Slf4j
@Component
@Aspect
public class LogAspect {

    private final Logger systemLog = Logger.getLogger("SystemLog");
    private final Logger businessLog = Logger.getLogger("BusinessLog");

//    @Pointcut("execution(* com.six.backend.service.*.*(..))")

    @Pointcut("@annotation(com.six.backend.annotation.OperLog)")
    public void pointCut(){

    }


    @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        handleLog(joinPoint, null);
    }


    @AfterReturning(pointcut = "pointCut()")
    public void after(JoinPoint joinPoint)
    {
        handleLog(joinPoint, null);
    }


    @AfterThrowing(value = "pointCut()", throwing = "e")
    public void doAfter(JoinPoint joinPoint, Exception e)
    {
        handleLog(joinPoint, e);
    }

    @Around("pointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint)
    {
        Object proceed=null;
        handleLog(joinPoint, null);
        try {
            proceed = joinPoint.proceed();
            return proceed;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

    private void handleLog(JoinPoint joinPoint, Exception e) {
        try
        {
            OperLog logger = giveController(joinPoint);

            if (logger == null)
            {
                return;
            }
            //获取request
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);

            String signature = joinPoint.getSignature().toString();
            //截取方法名
            String methodName = signature.substring(signature.lastIndexOf(".")+1, signature.indexOf("("));
            String longTemp = joinPoint.getStaticPart().toLongString();
            //通过调用方法的目标对象通过反射获取它的类名
            String classType = joinPoint.getTarget().getClass().getName();
            Class<?> clazz = Class.forName(classType);

            String clazzName = clazz.getName();

            OperLog process = LogAspect.process(joinPoint);

            Object[] args = joinPoint.getArgs();

            ArrayList<String> params = new ArrayList<>();

            for (Object arg : args) {
                params.add(String.valueOf(arg));
            }

            String ipAddr = ipUtils.getIpAddr(request);

            if(e != null) {
                systemLog.error(String.format("所在位置相关信息:[%s]",longTemp));
                systemLog.error(e);
            }
            businessLog.info(String.format("操作ip地址:[%s],模块:[%s],类型:[%s],描述:[%s],类名:[%s],方法名:[%s],入参:[%s]",
                    ipAddr,
                    process.operModule(),
                    process.operType(),
                    process.operDesc() ,
                    clazzName,
                    methodName,
                    params));
        } catch (Exception exp)
        {
            systemLog.error("异常信息:[%s]",exp);
            exp.printStackTrace();
        }
    }


    /**
     * 获得注解
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private static OperLog giveController(JoinPoint joinPoint) throws Exception {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null)
        {
            return method.getAnnotation(OperLog.class);
        }
        return null;
    }

    public static OperLog process(JoinPoint joinPoint) {

        return ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(OperLog.class);
    }
}
